﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.Common;
using Vbyte.SharpOrm.Config;

namespace Vbyte.SharpOrm
{
    /// <summary>
    /// 建立业务会话及事务支持
    /// </summary>
    /// <remarks>到达能够支持多个数据源建立起一个业务事务，实现回滚和提交。</remarks>
    //[ToDo("调试和完善")]
    public class BusinessSession : IDisposable
    {
        private DbConnection _currentConn = null;
        private List<DbConnection> _linkedConns = new List<DbConnection>();

        private DbTransaction _currentTransaction = null;
        private List<DbTransaction> _linkedTrans = new List<DbTransaction>();

        //private DbCommand _currentCmd = null;
        //private List<DbCommand> _linkedCmds = new List<DbCommand>();

        private Type _triggerType = null;
        private List<Type> _linkedTypes = new List<Type>();

        //如各个对象类型绑定的连接字符串名一样则共享数据库对象
        private bool _SharedDbObject = false;

        /// <summary>
        /// 各实例类型对应相关数据库对象的索引
        /// </summary>
        private Dictionary<Type, int> MapDict = new Dictionary<Type, int>();

        /// <summary>
        /// 键和值绑定的数据库对象为同一对象(触发对象)
        /// </summary>
        private Dictionary<Type, Type> MapDbDict = new Dictionary<Type, Type>();

        /// <summary>
        /// 从起始实例开始新建数据库会话
        /// </summary>
        /// <param name="triggerEntry">数据映射实例</param>
        public BusinessSession(TableEntry triggerEntry)
            : this(triggerEntry, Type.EmptyTypes)
        {

        }

        /// <summary>
        /// 新建数据库会话
        /// </summary>
        /// <param name="triggerEntry">触发的数据映射实例</param>
        /// <param name="otherEntryTypes">其他关联实例的类型</param>
        public BusinessSession(TableEntry triggerEntry, params Type[] otherEntryTypes)
        {
            _triggerType = triggerEntry.GetType();
            string currentKey = string.Empty;

            using (AdoNetAdapter adp = new AdoNetAdapter(triggerEntry))
            {
                _currentConn = adp.GetDbConnection();
                //_currentCmd = adp.GetDbCommand();
                currentKey = adp.GetTableBindConfig().BindConnectionKey;
            }

            if (otherEntryTypes != null && otherEntryTypes.Length > 0)
            {
                _SharedDbObject = true;

                AdoNetAdapter linkAdp = null;
                Dictionary<Type, string> connKeyDict = new Dictionary<Type, string>();

                int idx = 0;
                foreach (Type t in otherEntryTypes)
                {
                    //检查绑定连接字符
                    BindTableAttribute attrs = Attribute.GetCustomAttribute(t, typeof(BindTableAttribute)) as BindTableAttribute;
                    if (attrs == null)
                    {
                        throw new System.Configuration.ConfigurationErrorsException(String.Concat("类型[", t.FullName, "]上没有配置[", typeof(BindTableAttribute).FullName, "]属性！"));
                    }
                    else
                    {

                        bool blnFindSameKey = false;
                        foreach (Type cType in connKeyDict.Keys)
                        {
                            if (connKeyDict[cType].Equals(attrs.BindConnectionKey, StringComparison.InvariantCultureIgnoreCase)  && 
                                !cType.Equals(t))
                            {
                                MapDbDict[t] = cType;
                                blnFindSameKey = true;
                                break;
                            }
                        }

                        if (!blnFindSameKey)
                        {
                            connKeyDict[t] = attrs.BindConnectionKey;
                        }

                        if (!attrs.BindConnectionKey.Equals(currentKey))
                        {
                            _SharedDbObject = false;
                            //判断是否新建数据库连接对象
                            if (!MapDict.ContainsKey(t) && blnFindSameKey == false)
                            {
                                using (linkAdp = new AdoNetAdapter(attrs.BindConnectionKey))
                                {
                                    //_linkedCmds.Add(linkAdp.GetDbCommand());
                                    _linkedConns.Add(linkAdp.GetDbConnection());
                                }
                                MapDict.Add(t, idx);
                                idx++;
                            }
                        }
                        else
                        {
                            MapDbDict[t] = _triggerType;
                        }
                    }
                    _linkedTypes.Add(t);
                }
            }

            //Console.WriteLine("------新建实例------------");
            //foreach (Type tk in MapDict.Keys)
            //{
            //    Console.WriteLine(tk.FullName);
            //}

            //Console.WriteLine("------同一数据库------------");
            //foreach (Type tm in MapDbDict.Keys)
            //{
            //    Console.WriteLine("{0}={1}", tm, MapDbDict[tm]);
            //}

        }

        /// <summary>
        /// 开始数据库事务
        /// </summary>
        public void BeginTransaction()
        {
            RunAction<DbConnection>(_currentConn, _linkedConns, conn =>
            {
                if (conn == null)
                {
                    /*建立连接对象 开始事务*/
                    throw new ArgumentNullException("操作失败，没有可用的数据库连接对象！");
                }
                else
                {
                    //conn.Open();
                    //Console.WriteLine(conn.State.ToString());
                    if (conn.State == ConnectionState.Closed)
                    {
                        conn.Open();
                    }
                    if (_currentTransaction == null)
                    {
                        _currentTransaction = conn.BeginTransaction();
                    }
                    else
                    {
                        _linkedTrans.Add(conn.BeginTransaction());
                    }
                }
            }, true);
        }

        /// <summary>
        /// 从挂起状态回滚事务
        /// </summary>
        public void Rollback()
        {
            RunAction<DbTransaction>(_currentTransaction, _linkedTrans, tran => { if (tran != null) tran.Rollback(); }, false);
        }

        /// <summary>
        /// 提交数据库事务
        /// </summary>
        public void Commit()
        {
            RunAction<DbTransaction>(_currentTransaction, _linkedTrans, tran =>
            {
                if (tran != null) tran.Commit();
            }, false);
        }

        #region IDisposable 成员

        /// <summary>
        /// 运行一系列操作
        /// </summary>
        /// <typeparam name="T">操作对象类型</typeparam>
        /// <param name="tigger">触发对象</param>
        /// <param name="linked">相关联的对象</param>
        /// <param name="act">在对象类型上应用的动作</param>
        /// <param name="triggerFirst">触发对象是否最新运行</param>
        private void RunAction<T>(T tigger, List<T> linked, Action<T> act, bool triggerFirst)
        {
            if (tigger != null)
            {
                if (triggerFirst) act(tigger);
                foreach (T item in linked)
                {
                    act(item);
                }
                if (!triggerFirst) act(tigger);
            }
        }

        /// <summary>
        /// 获取ORM实例类型所关联的已建立连接的数据库连接对象
        /// </summary>
        /// <param name="instanceType">ORM实例类型</param>
        /// <returns>数据库连接对象</returns>
        public DbConnection GetTargetConnection(Type instanceType)
        {
            int idx = GetTargetDbIndex(instanceType);
            DbConnection conn = (idx == -1) ? _currentConn : _linkedConns[idx];
            return conn;
        }

        /// <summary>
        /// 获取ORM实例类型所关联的已建立连接的数据库事务
        /// </summary>
        /// <param name="instanceType">ORM实例类型</param>
        /// <returns>数据库事务</returns>
        public DbTransaction GetTargetTransaction(Type instanceType)
        {
            int idx = GetTargetDbIndex(instanceType);
            DbTransaction trans = (idx == -1) ? _currentTransaction : _linkedTrans[idx];
            return trans;
        }

        private int GetTargetDbIndex(Type instanceType)
        {
            //Console.WriteLine("获取类型:{0} 索引", instanceType.FullName);

            if (_SharedDbObject == false && !instanceType.Equals(_triggerType))
            {
                //同一数据连接键映射
                if (MapDbDict.ContainsKey(instanceType))
                {
                    instanceType = MapDbDict[instanceType];
                }

                //数据库对象索引词典
                if (MapDict.ContainsKey(instanceType))
                {
                    return MapDict[instanceType];
                }
            }
            return -1;
        }

        /// <summary>
        /// 执行与释放或重置非托管资源相关的应用程序定义的任务。
        /// </summary>
        public void Dispose()
        {
            //RunAction<DbCommand>(_currentCmd, _linkedCmds, cmd => { if (cmd != null) cmd.Dispose(); });
            RunAction<DbConnection>(_currentConn, _linkedConns, conn => { if (conn != null) { conn.Close(); conn.Dispose(); } }, false);
            RunAction<DbTransaction>(_currentTransaction, _linkedTrans, tran => { if (tran != null) tran.Dispose(); }, false);
        }

        #endregion


        #region 其他涉及事务包装的公共函数
        #region Select
        #endregion

        /* Insert Update Delete */

        #region insert
        /// <summary>
        /// 添加到相关数据库
        /// </summary>
        /// <param name="entry">表映射实例</param>
        /// <param name="getIdentity">是否获取最新生成的标志</param>
        /// <param name="NotIgnoreDefaultValueProperties">强制不忽略默认值的字段集合</param>
        /// <returns>如果获取标志键则返回标志值，否则为null。</returns>
        public object Insert(TableEntry entry, bool getIdentity, string[] NotIgnoreDefaultValueProperties)
        {
            Type instanceType = entry.GetType();
            return entry.Insert(GetTargetConnection(instanceType),
                getIdentity, NotIgnoreDefaultValueProperties, GetTargetTransaction(instanceType));
        }
        #endregion

        #region Update
        /// <summary>
        /// 更新数据库相关实例。
        /// </summary>
        /// <param name="entry">表映射实例</param>
        /// <param name="NotIgnoreDefaultValueProperties">强制不忽略默认值的字段集合</param>
        /// <returns>返回受影响的记录条数</returns>
        public int Update(TableEntry entry, string[] NotIgnoreDefaultValueProperties)
        {
            Type instanceType = entry.GetType();
            return entry.Update(NotIgnoreDefaultValueProperties,
                GetTargetConnection(instanceType),
                GetTargetTransaction(instanceType));
        }

        /// <summary>
        /// (忽略主键)依据旧有实例属性替换为新实例属性的值（更新满足条件的1个或多个记录）
        /// </summary>
        /// <param name="oldSqlTableEntry">满足实例的条件</param>
        /// <param name="newSqlTableEntry">新实例的值</param>
        /// <returns>返回受影响的记录条数</returns>
        public int ReplaceWith(TableEntry oldSqlTableEntry, TableEntry newSqlTableEntry)
        {
            Type instanceType = oldSqlTableEntry.GetType();
            return OrmHelper.ReplaceWith(oldSqlTableEntry, newSqlTableEntry,
                GetTargetConnection(instanceType),
                GetTargetTransaction(instanceType));
        }
        #endregion

        #region Delete

        /// <summary>
        /// 删除满足实例的所有记录
        /// </summary>
        /// <param name="entry">表映射实例</param>
        /// <param name="NotIgnoreDefaultValueProperties">强制不忽略默认值的字段集合</param>
        /// <returns>返回受影响的记录条数</returns>
        public int Delete(TableEntry entry, string[] NotIgnoreDefaultValueProperties)
        {
            Type instanceType = entry.GetType();
            return entry.Delete(GetTargetConnection(instanceType),
               NotIgnoreDefaultValueProperties,
               GetTargetTransaction(instanceType));
        }
        #endregion


        #endregion

    }

}
